package maple.util;

import java.lang.IllegalStateException;
import java.lang.IndexOutOfBoundsException;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.TreeMap;
import java.util.Vector;

import jade.domain.FIPAAgentManagement.Property;
import jade.domain.FIPAAgentManagement.ServiceDescription;

import maple.core.Gateway;
import maple.lang.Command;
import maple.lang.Language;

public class Profile extends Object
{
	public static enum Property{ PORT, TYPE, SENSITIVITY, LOCATION };

	protected boolean _available;
	protected TreeMap< String, String > _properties;

	/**
	 * construct a new profile with the specified content
	 *
	 * @param	content	KEY="VALUE"[ KEY="VALUE"]+
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Profile( final String content )
	{
		_properties = new TreeMap< String, String >();
		
		try
		{
			Matcher matcher = Pattern.compile( Language.EXPRESSION ).matcher( content );
			while( matcher.find() )
			{
				_properties.put( matcher.group( 1 ), matcher.group( 2 ) );
			}
		}
		catch( IllegalStateException e )
		{
		}
		catch( IndexOutOfBoundsException e )
		{
		}

		_available = isValid();
	}

	/**
	 * construct a new profile with the specified properties
	 *
	 * @param	properties	the specified properties
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Profile( final TreeMap< String, String > properties )
	{
		_properties = properties;
		_available = isValid();
	}

	/**
	 * construct a new task with the specified command
	 *
	 * @param	command		the specified command
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public Profile( final Command command )
	{
		_properties = new TreeMap< String, String >();
		
		Vector< String > parameters = command.getParameters();
		for( String parameter : parameters )
		{
			_properties.put( parameter, command.getParameter( parameter ) );
		}

		_available = isValid();
	}

	/**
	 * return true if this profile contains the specified property or null if this profile is not available 
	 *
	 * @param	property	the specified property
	 * @return	true if this task contains the specified property
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public boolean containsProperty( final String property )
	{
		if( _available )
		{
			return _properties.containsKey( property );
		}
		return false;
	}

	/**
	 * return the values of the specified property in this profile or null if this profile is not available
	 *
	 * @param	property	the specified property 
	 * @return	the values of the specified property
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public String getProperty( final String property )
	{
		if( _available )
		{
			return _properties.get( property );
		}
		return null;
	}

	/**
	 * return true if this profile is available
	 *
	 * @return	true if this profile is available
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public boolean isAvailable()
	{
		return _available;
	}

	/**
	 * return true if this profile is valid 
	 *
	 * @return	true if this profile is valid 
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	private boolean isValid()
	{
		// check the existence
		for( Profile.Property property : Profile.Property.values() )
		{
			if( !_properties.containsKey( property.toString() ) )
			{
				return false;
			}
		}

		// check the conditions
		for( String property : _properties.keySet() )
		{
			String value = _properties.get( property );
			try
			{
				switch( Profile.Property.valueOf( property ) )
				{
					case PORT:
						String[] token = value.split( Gateway.DELIMITER );
						if( token.length < 2 ) 
						{
							return false;
						}
						break;
					case TYPE:
						try
						{
							Actuator.Type.valueOf( value );
						}
						catch( IllegalArgumentException ea )
						{
							try
							{
								Sensor.Type.valueOf( value );
							}
							catch( IllegalArgumentException es )
							{
								return false;
							}
						}
						break;
					case SENSITIVITY:
						if( Integer.parseInt( value ) < 0 )
						{
							return false;
						}
						break;
					case LOCATION:
						break;
				}
			}
			catch( IllegalArgumentException e )
			{
				return false;
			}
		}
		return true;
	}

	/**
	 * return the operator of this profile of null if this profile is not available
	 *
	 * @param	name		the specified name
	 * @param	instruction	the specified instruction
	 * @param	port		the specified port
	 * @return	the operator of this profile
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	private String toOperator( final String name, final Language.Instruction instruction, final String port )
	{
		if( _available )
		{
			return name + Language.DELIMITER
				   + instruction.toString()
				   + Language.DELIMITER
				   + Profile.Property.PORT.toString()
				   + Language.ASSIGN
				   + Language.QUOTE
				   + port
				   + Language.QUOTE;
		}
		return null;
	}

	/**
	 * return the precondition of this profile of null if this profile is not available
	 *
	 * @return	the precondition of this profile
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	private String toPrecondition()
	{
		if( _available )
		{
			String location = Profile.Property.LOCATION.toString();
			return location + Language.ASSIGN
				   + Language.QUOTE + _properties.get( location ) + Language.QUOTE;
		}
		return null;
	}

	/**
	 * return the postcondition of this profile of null if this profile is not available
	 *
	 * @return	the postcondition of this profile
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public String toPostcondition()
	{
		if( _available )
		{
			return _properties.get( Profile.Property.TYPE.toString() );
		}
		return null;
	}

	/**
	 * return the service description of this profile of null if this profile is not available
	 *
	 * @param	name		the specified name
	 * @param	instruction	the specified instruction
	 * @param	port		the specified port
	 * @return	the service description of this command 
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public ServiceDescription toService( final String name, final Language.Instruction instruction, final String port )
	{
		if( _available )
		{
			ServiceDescription service = new ServiceDescription();
			service.addLanguages( Language.class.getName() );
			service.setName( instruction.toString() );
		
			// for planner
			service.addProperties( new jade.domain.FIPAAgentManagement.Property( Planner.Property.OPERATOR.toString(), toOperator( name, instruction, port ) ) );
			service.addProperties( new jade.domain.FIPAAgentManagement.Property( Planner.Property.PRECONDITION.toString(), toPrecondition() ) );
			service.addProperties( new jade.domain.FIPAAgentManagement.Property( Planner.Property.POSTCONDITION.toString(), toPostcondition() ) );
			
			for( String property : _properties.keySet() )
			{
				String value = _properties.get( property );
				
				try
				{
					switch( Profile.Property.valueOf( property ) )
					{
						case PORT:
							service.setOwnership( value );
							break;
						case TYPE:
							service.setType( value );
							break;
						default:
							service.addProperties( new jade.domain.FIPAAgentManagement.Property( property.toString(), value ) );
					}
				}
				catch( IllegalArgumentException e )
				{
					service.addProperties( new jade.domain.FIPAAgentManagement.Property( property.toString(), value ) );
				}
			}

			return service;
		}
		return null;
	}

	/**
	 * return the String representation of this command or null if this profile is not available
	 *
	 * @return	the String representation of this profile
	 *
	 * @author	snowfoxy
	 * @since	2011-11-15
	 */
	public String toString()
	{
		if( _available )
		{
			String profile = "";
			for( String property : _properties.keySet() )
			{
				profile = profile
						  + Language.DELIMITER
						  + property
						  + Language.ASSIGN
						  + Language.QUOTE
						  + _properties.get( property )
						  + Language.QUOTE;
			}

			if( profile.length() > 0 )
			{
				profile = profile.substring( 1 );
			}

			return profile;
		}
		return null;
	}
}
